/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id$
 */

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR( void );
static void prvvUARTRxISR( void );

/* ----------------------- Start implementation -----------------------------*/

// 毫秒级的延时
void rs485_delay_ms(uint16_t time)
{    
    uint16_t i = 0;  
    
    while( time-- )
    {
        i = 12000;  //自己定义
        while(i--);    
    }
}

// 使能或失能串口，需要根据不同的STM32的移植
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
    ENTER_CRITICAL_SECTION( );
    
    if( xRxEnable )
    {
#if (MODBUS_USE_RS485 == TRUE)
        GPIO_WriteBit(MODBUS_RS485_EN_PORT, MODBUS_RS485_EN_PIN, Bit_RESET);    // RS485_EN 接收
        rs485_delay_ms(5);
#endif 
        
        USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);
    }
    else
    {
       USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);
    }
	
    if( xTxEnable )
    {
#if (MODBUS_USE_RS485 == TRUE)
        GPIO_WriteBit(MODBUS_RS485_EN_PORT, MODBUS_RS485_EN_PIN, Bit_SET);    // RS485_EN 发送
        rs485_delay_ms(5);
#endif 
        
		USART_ITConfig(MODBUS_USART, USART_IT_TC, ENABLE);    //完成中断模式传输
    }
    else
    {
		USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);   //完成中断模式传输
    }
    
    EXIT_CRITICAL_SECTION( );
}


// 串口初始化，需要根据不同的STM32的移植
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    BOOL bInitialized = TRUE;
	GPIO_InitTypeDef GPIO_InitStruct; 
	USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef  NVIC_InitStructure;  
    
	MODBUS_USART_CLK_CMD(MODBUS_USART_CLK, ENABLE);             // 使能串口时钟
	MODBUS_USART_GPIO_CLK_CMD(MODBUS_USART_GPIO_CLK, ENABLE);   // 使能GPIO时钟
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = MODBUS_USART_TX_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(MODBUS_USART_TX_PORT, &GPIO_InitStruct);  // TX配置

	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin = MODBUS_USART_RX_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(MODBUS_USART_RX_PORT, &GPIO_InitStruct);  // RX配置

	USART_InitStructure.USART_BaudRate = ulBaudRate;    // 波特率
    
	switch ( eParity )  // 校验位
	{
	case MB_PAR_NONE:
		USART_InitStructure.USART_Parity = USART_Parity_No;
		break;
	case MB_PAR_ODD:
		USART_InitStructure.USART_Parity = USART_Parity_Odd;
		break;
	case MB_PAR_EVEN:
		USART_InitStructure.USART_Parity = USART_Parity_Even;
		break;
    default:
		bInitialized = FALSE;
	}
    
	switch ( ucDataBits )   // 数据位
	{
	case 8:
		if(eParity == MB_PAR_NONE)
            USART_InitStructure.USART_WordLength = USART_WordLength_8b;
		else
            USART_InitStructure.USART_WordLength = USART_WordLength_9b;
		break;
	case 7:
		break;
	default:
		bInitialized = FALSE;
	}
	
	if( bInitialized )
	{
		ENTER_CRITICAL_SECTION( );
        
		USART_InitStructure.USART_StopBits = USART_StopBits_1;  // 停止位
		USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 流控
		USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发一体
		USART_Init(MODBUS_USART, &USART_InitStructure);
        USART_Cmd(MODBUS_USART, ENABLE);
        
		EXIT_CRITICAL_SECTION( );
	}
    
    // 中断配置
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);             // 中断组配置 
    NVIC_InitStructure.NVIC_IRQChannel = MODBUS_USART_IRQ ;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0 ;  // 抢占优先级0  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          // 子优先级0  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // IRQ通道使能  
    NVIC_Init(&NVIC_InitStructure);                             // 根据指定的参数初始化VIC寄存器  

#if (MODBUS_USE_RS485 == TRUE)
    MODBUS_RS485_EN_GPIO_CLK_CMD(MODBUS_RS485_EN_GPIO_CLK, ENABLE); // 使能 485_EN 引脚的时钟
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = MODBUS_RS485_EN_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(MODBUS_RS485_EN_PORT, &GPIO_InitStruct);  // RS485_EN 引脚配置
    
    GPIO_WriteBit(MODBUS_RS485_EN_PORT, MODBUS_RS485_EN_PIN, Bit_RESET);    // RS485_EN 接收
#endif    
    
	return bInitialized;    // 返回初始化结果
}


// 串口发送一个字节，需要根据不同的STM32的移植
BOOL xMBPortSerialPutByte( CHAR ucByte )
{
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
    
	USART_SendData(MODBUS_USART, (uint16_t)ucByte);
	
	return TRUE;
}


// 串口接收一个字节，需要根据不同的STM32的移植
BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
    *pucByte = (uint8_t)USART_ReceiveData(MODBUS_USART);	//获取接收BUFFER的数据
    
    return TRUE;
}

/* Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call 
 * xMBPortSerialPutByte( ) to send the character.
 */
static void prvvUARTTxReadyISR( void )
{
    pxMBFrameCBTransmitterEmpty(  );
}

/* Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 */
static void prvvUARTRxISR( void )
{
    pxMBFrameCBByteReceived(  );
}

// 串口的中断处理函数
void MODBUS_USART_IRQHandler(void)
{
	if( USART_GetITStatus(MODBUS_USART,USART_IT_TC) != RESET )		//完成中断模式传输
	{
	    prvvUARTTxReadyISR();
		USART_ClearITPendingBit(MODBUS_USART, USART_IT_TC); 
	}
	else if( USART_GetITStatus(MODBUS_USART,USART_IT_RXNE) != RESET )
	{
		prvvUARTRxISR();
		USART_ClearITPendingBit(MODBUS_USART, USART_IT_RXNE); 
	}
}
